﻿using System.Reflection;
using ApiConceptsFramework.FluentApi;
using NUnit.Framework;
using DIApi.Xml;
using test.ApiConceptsFramework.FluentApi.Scenarios.TestClasses;

namespace test.ApiConceptsFramework.FluentApi.Scenarios
{
	[TestFixture, Explicit]
	public class DiApiXmlTests
	{
		private Assembly[] _assemblies;
		private string[] _usings;
		private ApiEvaluator _eval;

		#region Setup
		[SetUp]
		public void SetUp()
		{
			_assemblies = new[] { typeof(DIContainer).Assembly };
			_usings = new[] { "DIApi.Xml" };

			_eval = new ApiEvaluator(_assemblies);
			_eval.SetUsings(_usings);
			_eval.SetAssemblyRefs(_assemblies);
		}

		[TearDown]
		public void TearDown()
		{
			_eval.EvaluateAndPrintToConsole();
		}
		#endregion

		[Test]
		public void Simple_Binding()
		{
			//var container = new DIContainer("configFilename);
			//var instance = container.Get<IFoo>();

			_eval.NumberOfImplementationClasses(2);
			//_eval.ClassUsage<DIContainer>();
			_eval.Instantiation(() => new DIContainer("filename")).RequiresInformationLookup();
			_eval.MethodCall((DIContainer c) => c.Get<IFoo>());

			//3E, 2A
			//<DIContainer>
			//  <Bindings>
			//    <Binding type="TestClasses.IFoo" bindTo="TestClasses.Foo"/>
			//  </Bindings>
			//</DIContainer>

			_eval.XmlConfigurationFile("config.xml", false);
			_eval.XmlElement("DIContainer");
			_eval.XmlElement("Bindings");
			_eval.XmlElement("Binding")
				.XmlAttribute("type", needsLookupOfExternalInformation:true)
				.XmlAttribute("bindTo", needsLookupOfExternalInformation:true);
		}

		[Test]
		public void Named_Bindings()
		{
			//var container = new DIContainer("configFilename);
			//var instance1 = container.Get<IFoo>("t1");
			//var instance2 = container.Get<IFoo>("t2");

			_eval.NumberOfImplementationClasses(2);
			//_eval.ClassUsage<DIContainer>(1, 1);
			_eval.Instantiation(() => new DIContainer("filename"), 1, 1).RequiresInformationLookup();
			_eval.MethodCall((DIContainer c) => c.Get<IFoo>("name"), 2);

			//4E, 6A
			//<DIContainer>
			//  <Bindings>
			//    <Binding type="TestClasses.IFoo" bindTo="TestClasses.Foo" name="t1"/>
			//    <Binding type="TestClasses.IFoo" bindTo="TestClasses.Foo2" name="t2"/>
			//  </Bindings>
			//</DIContainer>

			_eval.NumberOfImplementationClasses(2);
			_eval.XmlConfigurationFile("config.xml", false);
			_eval.XmlElement("DIContainer", 1, 1);
			_eval.XmlElement("Bindings", 1, 1);
			_eval.XmlElement("Binding", 2, 1)
				.XmlAttribute("type", 2, 1, true).XmlAttribute("bindTo", 2, 1, true).XmlAttribute("name", 2);
		}

		[Test]
		public void Ctor_Injection_Simple()
		{
			//var container = new DIContainer("configFilename);
			//var instance = container.Get<Service>();

			_eval.NumberOfImplementationClasses(2);
			//_eval.ClassUsage<DIContainer>(1, 2);
			_eval.Instantiation(() => new DIContainer("filename"), 1, 2).RequiresInformationLookup();
			_eval.MethodCall((DIContainer c) => c.Get<Service>(), 1, 1);

			//7E, 4A
			//<DIContainer>
			//  <Bindings>
			//    <Binding type="TestClasses.IFoo" bindTo="TestClasses.Foo" />
			//  </Bindings>
			//  <Instantiations>
			//    <Instantiation type="TestClasses.Service>
			//      <Constructor><Param name="bla/></Constructor>
			//    </Instantiation>
			//  </Instantiations>
			//</DIContainer>

			_eval.NumberOfImplementationClasses(2);
			_eval.XmlConfigurationFile("config.xml", false);
			_eval.XmlElement("DIContainer", 1, 2);
			_eval.XmlElement("Bindings", 1, 2);
			_eval.XmlElement("Binding", 1, 2)
				.XmlAttribute("type", 1, 2, true).XmlAttribute("bindTo", 1, 2, true);
			_eval.XmlElement("Instantiations");
			_eval.XmlElement("Instantiation").XmlAttribute("type", 1, 0, true);
			_eval.XmlElement("Constructor");
			_eval.XmlElement("Param").XmlAttribute("name", 1, 0, true);
		}

		[Test]
		public void Ctor_Injection_Complex()
		{
			//var container = new DIContainer("configFilename);
			//var instance = container.Get<Service>();

			_eval.NumberOfImplementationClasses(2);
			//_eval.ClassUsage<DIContainer>(1, 3);
			_eval.Instantiation(() => new DIContainer("filename"), 1, 3).RequiresInformationLookup();
			_eval.MethodCall((DIContainer c) => c.Get<Service>(), 1, 2);

			//9E, 9A
			//<DIContainer>
			//  <Bindings>
			//    <Binding type="TestClasses.IFoo" bindTo="TestClasses.Foo" />
			//    <Binding type="TestClasses.IFoo" bindTo="TestClasses.Foo" />
			//  </Bindings>
			//  <Instantiations>
			//    <Instantiation type="TestClasses.Service>
			//      <Constructor><Param name="bla1" binding="t1"/><Param name="bla2" binding="t2"/></Constructor>
			//    </Instantiation>
			//  </Instantiations>
			//</DIContainer>

			_eval.NumberOfImplementationClasses(2);
			_eval.XmlConfigurationFile("config.xml", false);
			_eval.XmlElement("DIContainer", 1, 3);
			_eval.XmlElement("Bindings", 1, 3);
			_eval.XmlElement("Binding", 2, 4)
				.XmlAttribute("type", 2, 4, true).XmlAttribute("bindTo", 2, 4, true).XmlAttribute("name",2,2);
			_eval.XmlElement("Instantiations",1,1);
			_eval.XmlElement("Instantiation", 1, 1).XmlAttribute("type", 1, 1, true);
			_eval.XmlElement("Constructor", 1, 1);
			_eval.XmlElement("Param", 2, 1).XmlAttribute("name", 2, 1, true).XmlAttribute("binding", 2);
		}

		[Test]
		public void Binding_with_singleton_scope()
		{
			//var container = new DIContainer("configFilename);
			//var instance = container.Get<IFoo>();

			_eval.NumberOfImplementationClasses(2);
			//_eval.ClassUsage<DIContainer>(1, 4);
			_eval.Instantiation(() => new DIContainer("filename"), 1, 4).RequiresInformationLookup();
			_eval.MethodCall((DIContainer c) => c.Get<IFoo>(),2,3);

			//3E, 3A
			//<DIContainer>
			//  <Bindings>
			//    <Binding type="TestClasses.IFoo" bindTo="TestClasses.Foo" scope="singleton"/>
			//  </Bindings>
			//</DIContainer>

			_eval.XmlConfigurationFile("config.xml", false);
			_eval.XmlElement("DIContainer", 1, 4);
			_eval.XmlElement("Bindings",1,4);
			_eval.XmlElement("Binding", 1, 6)
			     .XmlAttribute("type", 1, 6, true).XmlAttribute("bindTo", 1, 6, true).XmlAttribute("scope");
		}

		[Test]
		public void All()
		{
			//var container = new DIContainer("configFilename);
			//var instance = container.Get<Service>();

			_eval.NumberOfImplementationClasses(2);
			//_eval.ClassUsage<DIContainer>(1, 5);
			_eval.Instantiation(() => new DIContainer("filename"), 1, 5).RequiresInformationLookup();
			_eval.MethodCall((DIContainer c) => c.Get<Service>(), 1, 4);

			//15 E, 20 A, 12 lookup
			//<DIContainer>
			//  <Bindings>
			//    <Binding type="TestClasses.ILogger" bindTo="TestClasses.ConsoleLogger" name="console" scope="Singleton" />
			//    <Binding type="TestClasses.ILogger" bindTo="TestClasses.FileLogger" name="file" />
			//    <Binding type="TestClasses.IAuthenticator" bindTo="TestClasses.WindowsBasedAuthenticator" name="auth" />
			//  </Bindings>
			//  <Instantiations>
			//    <Instantiation type="TestClasses.Service">
			//      <Constructor>
			//        <Param name="consoleLogger" binding="console"/>
			//        <Param name="fileLogger" binding="file"/>
			//        <Param name="authenticator" binding="auth"/>
			//      </Constructor>
			//    </Instantiation>
			//    <Instantiation type="TestClasses.WindowsBasedAuthenticator">
			//      <Constructor>
			//        <Param name="logger" binding="console"/>
			//      </Constructor>
			//    </Instantiation>
			//  </Instantiations>
			//</DIContainer>

			_eval.XmlConfigurationFile("config.xml", false);
			_eval.XmlElement("DIContainer", 1, 5);
			_eval.XmlElement("Bindings", 1, 5);
			_eval.XmlElement("Binding", 3, 6)
				.XmlAttribute("type", 3, 6, true).XmlAttribute("bindTo", 3, 6, true).XmlAttribute("name", 3, 2).XmlAttribute("scope", 1, 1);
			_eval.XmlElement("Instantiations", 1, 2);
			_eval.XmlElement("Instantiation", 2, 2).XmlAttribute("type", 2, 2, true);
			_eval.XmlElement("Constructor", 2, 2, true);
			_eval.XmlElement("Param", 4, 3).XmlAttribute("name", 4, 3, true).XmlAttribute("binding", 4, 2);
		}
	}
}
